﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Newtonsoft.Json;
using HigLabo.Core;

namespace HigLabo.Net.Dropbox
{
    public partial class DropboxClient
    {
        private String GetResponseBodyText(String url, DropboxCommand command)
        {
            return this.GetResponseBodyText(HttpMethodName.Get, url, command);
        }
        private String GetResponseBodyText(HttpMethodName methodName, String url, DropboxCommand command)
        {
            var u = _protocol.ToString().ToLower() + "://" + url;
            IDictionary<String, String> d = new Dictionary<String, String>();

            if (methodName == HttpMethodName.Put ||
                methodName == HttpMethodName.Delete)
            { throw new ArgumentException(); }

            if (command != null)
            {
                d = command.GetParameters();
            }
            var cm = this.CreateHttpRequestCommand(methodName, u, d);
            var res = this.GetResponse(cm);
            if (res.StatusCode != HttpStatusCode.OK)
            {
                throw new DropboxResponseException(res);
            }
            return res.BodyText;
        }
        private Stream GetHttpWebResponse(HttpMethodName methodName, String url, DropboxCommand command)
        {
            String u = _protocol.ToString().ToLower() + "://" + url;
            IDictionary<String, String> d = new Dictionary<string, string>();

            if (methodName == HttpMethodName.Put ||
                methodName == HttpMethodName.Delete)
            { throw new ArgumentException(); }

            if (command != null)
            {
                d = command.GetParameters();
            }
            var cm = this.CreateHttpRequestCommand(methodName, url, d);
            var res = this.GetHttpWebResponse(cm);
            return res.GetResponseStream();
        }
        private String Post(String url, UploadFileCommand command)
        {
            var u = _protocol.ToString().ToLower() + "://" + url;
            var d = command.GetParameters();
            d["file"] = command.FileName;
            var cm = this.CreateHttpRequestCommand(HttpMethodName.Post, u, d);
            var boundary = UploadFileCommand.GetBoundaryString();
            cm.ContentType = "multipart/form-data; boundary=\"" + boundary + "\"";
            cm.SetBodyStream(command.CreatePostData(boundary));

            return this.GetResponse(cm).BodyText;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public AccountInfo GetAccountInfo()
        {
            return this.GetAccountInfo(null);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public AccountInfo GetAccountInfo(GetAccountCommand command)
        {
            var text = this.GetResponseBodyText(DropboxApiUrl.Version1.GetAccount, command);
            return new AccountInfo(text);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public String CreateAccount(CreateAccountCommand command)
        {
            return this.GetResponseBodyText(DropboxApiUrl.Version1.CreateAccount, command);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public Metadata GetMetadata(GetMetadataCommand command)
        {
            var text = this.GetResponseBodyText(DropboxApiUrl.GetApiUrl(DropboxApiUrl.Version1.Metadata
                                                                        , this.Root.ToStringFromEnum()
                                                                        , command.Path)
                                                , command);
            return new Metadata(text);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public Metadata GetMetadata(String path)
        {
            var cm = new GetMetadataCommand();
            cm.Path = path;
            return this.GetMetadata(cm);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public Byte[] GetFile(GetFileCommand command)
        {
            if (String.IsNullOrEmpty(command.Path)) { throw new ArgumentException(); }
            var str = this.GetHttpWebResponse(HttpMethodName.Get
                                                 , DropboxApiUrl.GetApiUrl(DropboxApiUrl.Version1.Files, this.Root.ToStringFromEnum(), command.Path)
                                                 , command);
            return str.ToByteArray();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public Byte[] GetFile(String path)
        {
            var cm = new GetFileCommand();
            cm.Path = path;
            return this.GetFile(cm);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        public Metadata UploadFile(UploadFileCommand command)
        {
            if (String.IsNullOrEmpty(command.FolderPath)) { throw new ArgumentException(); }
            var url = DropboxApiUrl.GetApiUrl(DropboxApiUrl.Version1.Files, this.Root.ToStringFromEnum(), command.FolderPath);
            return new Metadata(this.Post(url, command));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public Byte[] GetThumbnails(GetThumbnailsCommand command)
        {
            var str = this.GetHttpWebResponse(HttpMethodName.Get
                                , DropboxApiUrl.GetApiUrl(DropboxApiUrl.Version1.Thumbnail, this.Root.ToStringFromEnum(), command.Path)
                                , command);
            return str.ToByteArray();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <param name="size"></param>
        /// <param name="format"></param>
        /// <returns></returns>
        public Byte[] GetThumbnails(String path, ThumbnailSize size, ThumbnailFormat format)
        {
            var cm = new GetThumbnailsCommand();
            cm.Path = path;
            cm.Size = size;
            cm.Format = format;
            return this.GetThumbnails(cm);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public Metadata CopyFile(CopyFileCommand command)
        {
            var text = this.GetResponseBodyText(HttpMethodName.Post, DropboxApiUrl.Version1.CopyFile, command);
            return new Metadata(text);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fromPath"></param>
        /// <param name="toPath"></param>
        /// <returns></returns>
        public Metadata CopyFile(String fromPath, String toPath)
        {
            var cm = new CopyFileCommand();
            cm.FromPath = fromPath;
            cm.ToPath = toPath;
            return this.CopyFile(cm);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public Metadata MoveFile(MoveFileCommand command)
        {
            var text = this.GetResponseBodyText(HttpMethodName.Post, DropboxApiUrl.Version1.MoveFile, command);
            return new Metadata(text);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fromPath"></param>
        /// <param name="toPath"></param>
        /// <returns></returns>
        public Metadata MoveFile(String fromPath, String toPath)
        {
            var cm = new MoveFileCommand();
            cm.FromPath = fromPath;
            cm.ToPath = toPath;
            return this.MoveFile(cm);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public Metadata CreateFolder(CreateFolderCommand command)
        {
            var text = this.GetResponseBodyText(HttpMethodName.Post, DropboxApiUrl.Version1.CreateFolder, command);
            return new Metadata(text);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public Metadata CreateFolder(String path)
        {
            var cm = new CreateFolderCommand();
            cm.Path = path;
            return this.CreateFolder(cm);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public Metadata Delete(DeleteCommand command)
        {
            var text = this.GetResponseBodyText(HttpMethodName.Post, DropboxApiUrl.Version1.Delete, command);
            return new Metadata(text);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public Metadata Delete(String path)
        {
            var cm = new DeleteCommand();
            cm.Path = path;
            return this.Delete(cm);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public Metadata PutFile(PutFileCommand command)
        {
            if (String.IsNullOrEmpty(command.Path)) { throw new ArgumentException(); }
            var url = DropboxApiUrl.GetApiUrl(DropboxApiUrl.Version1.FilesPut, this.Root.ToStringFromEnum(), command.Path);
            url = _protocol.ToString().ToLower() + "://" + url;
            var cm = this.CreateHttpRequestCommand(HttpMethodName.Put, url, command.GetParameters());
            cm.SetBodyStream(command.CreatePutData());
            return new Metadata(this.GetResponse(cm).BodyText);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public List<Metadata> GetRevisions(GetRevisionsCommand command)
        {
            var text = this.GetResponseBodyText(HttpMethodName.Get
                    , DropboxApiUrl.GetApiUrl(DropboxApiUrl.Version1.Revision, this.Root.ToStringFromEnum(), command.Path)
                    , command);
            return GetMetadataList(text);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public List<Metadata> Search(SearchCommand command)
        {
            var text = this.GetResponseBodyText(HttpMethodName.Post
                    , DropboxApiUrl.GetApiUrl(DropboxApiUrl.Version1.Search, this.Root.ToStringFromEnum(), command.Path)
                    , command);
            return GetMetadataList(text);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <param name="query"></param>
        /// <returns></returns>
        public List<Metadata> Search(String path, String query)
        {
            var cm = new SearchCommand();
            cm.Path = path;
            cm.Query = query;
            return this.Search(cm);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public Metadata Restore(RestoreCommand command)
        {
            var text = this.GetResponseBodyText(HttpMethodName.Post
                , DropboxApiUrl.GetApiUrl(DropboxApiUrl.Version1.Restore, this.Root.ToStringFromEnum(), command.Path)
                , command);
            return new Metadata(text);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public LinkInfo GetShareableLink(GetShareableLinkCommand command)
        {
            var text = this.GetResponseBodyText(HttpMethodName.Post
                , DropboxApiUrl.GetApiUrl(DropboxApiUrl.Version1.Share, this.Root.ToStringFromEnum(), command.Path)
                , command);
            return new LinkInfo(text);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public LinkInfo GetShareableLink(String path)
        {
            var cm = new GetShareableLinkCommand();
            cm.Path = path;
            return this.GetShareableLink(cm);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public LinkInfo GetMediaLink(GetMediaLinkCommand command)
        {
            var text = this.GetResponseBodyText(HttpMethodName.Post
                , DropboxApiUrl.GetApiUrl(DropboxApiUrl.Version1.Media, this.Root.ToStringFromEnum(), command.Path)
                , command);
            return new LinkInfo(text);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public LinkInfo GetMediaLink(String path)
        {
            var cm = new GetMediaLinkCommand();
            cm.Path = path;
            return this.GetMediaLink(cm);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public CopyRefInfo GetCopyRef(GetCopyRefCommand command)
        {
            var text = this.GetResponseBodyText(HttpMethodName.Get
                , DropboxApiUrl.GetApiUrl(DropboxApiUrl.Version1.CopyRef, this.Root.ToStringFromEnum(), command.Path)
                , command);
            return new CopyRefInfo(text);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public DeltaInfo GetDelta(GetDeltaCommand command)
        {
            var text = this.GetResponseBodyText(HttpMethodName.Post
                , DropboxApiUrl.GetApiUrl(DropboxApiUrl.Version1.Delta, "")
                , command);
            return new DeltaInfo(text);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public Metadata GetRootFolder()
        {
            return this.GetMetadata("/");
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<Metadata> GetRootItems()
        {
            var md = this.GetMetadata("/");
            return md.Contents;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<Metadata> GetRootFolders()
        {
            var md = this.GetMetadata("/");
            md.Contents.RemoveAll(el => el.IsDirectory == false);
            return md.Contents;
        }
    }
}
